﻿using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using MainGameModule.ViewModels;
using MainGameModule.Views;
using YourWorld.Common;
using MainGameModule.Models;
using YourWorld.Common.Infrastucture.Events;
using BusinessObjects;
using System.Collections.ObjectModel;
using System.Linq;
using System;
using System.Diagnostics;
using YourWorld.Common.GreenWorldEntitiesService;

namespace MainGameModule
{
    public class GameModule : IModule
    {
        private IUnityContainer container;
        private IRegionManager regionManager;
        private IGameModel gameModel;

        private Player player;
        private GameMode gameMode;

        public Player Player
        {
            get
            {
                return this.player;
            }
        }

        public GameModule( IRegionManager regionManager, IUnityContainer container )
        {
            this.regionManager = regionManager;
            this.container = container;

            this.SubscribeForEvents();
        }

        public void Initialize()
        {
            this.RegisterServices();
            this.RegisterTypes();

            this.gameModel = this.container.Resolve<IGameModel>();
        }

        public void StartSinglePlayerGame( Player player )
        {
            if ( player.IsGuest == false )
            {
                Player loadedPlayer = PlayersRepository.Instance.GetPlayerByUserId( player.UserId );
                Debug.Assert( loadedPlayer != null, "No user with id = " + player.UserId + " in the local DB!" );

                player.BadgesCollection = loadedPlayer.BadgesCollection;
                player.QuestsCollection = loadedPlayer.QuestsCollection;
                player.Connections = loadedPlayer.Connections;
                player.Level = loadedPlayer.Level;
            }

            //Workaround for single player!
            player.MapId = player.MapId + 100;

            this.player = player;
            this.player.X = Consts.InitialPlayerX;
            this.player.Y = Consts.InitialPlayerY;

            this.gameMode = GameMode.SinglePlayer;
            string playerDir = ( ( CharacterTypes )player.KidControlId ).ToString();
            player.Image = String.Format( "/MainGameModule;component/Images/{0}/{1}/kid000{2}.png", playerDir, "left", 1 );

            GameMainViewModel gameMain = this.container.Resolve<GameMainViewModel>();
            gameMain.Player = this.player;
            RubbishBinsRepository.Instance.Player = this.player;

            this.gameModel.Subscribe( player );
            this.StartGame();
        }

        public void StartMultiplayerGame( Player player )
        {
            if ( player.IsGuest == false )
            {
                Player loadedPlayer = PlayersRepository.Instance.GetPlayerByUserId( player.UserId );
                Debug.Assert( loadedPlayer != null, "No user with id = " + player.UserId + " in the local DB!" );

                player.BadgesCollection = loadedPlayer.BadgesCollection;
                player.QuestsCollection = loadedPlayer.QuestsCollection;
                player.Connections = loadedPlayer.Connections;
                player.Level = loadedPlayer.Level;
            }

            this.player = player;
            this.player.X = Consts.InitialPlayerX;
            this.player.Y = Consts.InitialPlayerY;

            this.gameMode = GameMode.MultiPlayer;
            string playerDir = ( ( CharacterTypes )player.KidControlId ).ToString();
            player.Image = String.Format( "/MainGameModule;component/Images/{0}/{1}/kid000{2}.png", playerDir, "left", 1 );

            GameMainViewModel gameMain = this.container.Resolve<GameMainViewModel>();
            gameMain.Player = this.player;
            RubbishBinsRepository.Instance.Player = this.player;

            this.gameModel.Subscribe( player );
            this.StartGame();
        }

        public void MovePlayer( MoveDirection direction )
        {
            GameMainViewModel gameMain = this.container.Resolve<GameMainViewModel>();
            if ( gameMain.Player == null )
                return;

            double x = gameMain.Player.X;
            double y = gameMain.Player.Y;

            switch ( direction )
            {
                case MoveDirection.Down: y -= Consts.PlayerStep; break;
                case MoveDirection.Up: y += Consts.PlayerStep; break;
                case MoveDirection.Left: x -= Consts.PlayerStep; break;
                case MoveDirection.Right: x += Consts.PlayerStep; break;
            }

            foreach ( Player p in gameMain.Players )
            {
                if ( p.Id != gameMain.Player.Id && p.X == x && p.Y == y )
                    return;
            }

            int gridx = gameMain.Game.Grid.Count - ( int )( y / Consts.MapTileWidth ) - 1;
            int gridy = ( int )( x / Consts.MapTileHeight );

            if ( gridx <= 0 || gridy < 0 || gridx >= gameMain.Game.Grid.Count || gridy >= gameMain.Game.Grid[gridx].Count ||
                gameMain.Game.Grid[gridx][gridy] == Consts.Unreachable )
            {
                return;
            }

            MoveMessage message = new MoveMessage()
            {
                MoveDirection = direction,
                PlayerId = this.player.Id,
                Steps = Consts.PlayerStep
            };

            this.gameModel.PublishMovePlayerMessage( message );
        }

        public void MovePlayer( SimplePoint point )
        {
            GameMainViewModel gameMain = this.container.Resolve<GameMainViewModel>();
            if ( gameMain.Player == null )
                return;

            double x = gameMain.Player.X + point.X;
            double y = gameMain.Player.Y + point.Y;

            //switch ( direction )
            //{
            //    case MoveDirection.Down: y -= Consts.PlayerStep; break;
            //    case MoveDirection.Up: y += Consts.PlayerStep; break;
            //    case MoveDirection.Left: x -= Consts.PlayerStep; break;
            //    case MoveDirection.Right: x += Consts.PlayerStep; break;
            //}

            foreach ( Player p in gameMain.Players )
            {
                if ( p.Id != gameMain.Player.Id && p.X == x && p.Y == y )
                    return;
            }

            int gridx = gameMain.Game.Grid.Count - ( int )( y / Consts.MapTileWidth ) - 1;
            int gridy = ( int )( x / Consts.MapTileHeight );

            if ( gridx <= 0 || gridy < 0 || gridx >= gameMain.Game.Grid.Count || gridy >= gameMain.Game.Grid[gridx].Count ||
                gameMain.Game.Grid[gridx][gridy] == Consts.Unreachable )
            {
                return;
            }

            MoveMessage message = new MoveMessage()
            {
                X = x,
                Y = y,
                PlayerId = this.player.Id,
                Steps = Consts.PlayerStep
            };

            this.gameModel.PublishMovePlayerMessage( message );
        }

        public void NewRubbish( Rubbish rubbish )
        {
            PlayerStatusManager.Instance.PauseGame();

            PopUpTrashViewModel vm = this.container.Resolve<PopUpTrashViewModel>();
            vm.Rubbish = rubbish;
            vm.BinItems = RubbishBinsRepository.Instance.PlayerBinsCollection;

            if ( this.regionManager.Regions[RegionNames.GamePopUpRegion].Views.Contains( this.container.Resolve<PopUpTrashView>() ) == false )
                this.regionManager.RegisterViewWithRegion( RegionNames.GamePopUpRegion, typeof( PopUpTrashView ) );

            this.regionManager.Regions[RegionNames.GamePopUpRegion].Activate( this.container.Resolve<PopUpTrashView>() );
        }

        public void ConnectWithPlayer( int playerId )
        {
            PlayerStatusManager.Instance.PauseGame();

            GameMainView mainView = this.container.Resolve<GameMainView>();

            ConnectViewModel vm = this.container.Resolve<ConnectViewModel>();
            vm.Initialize();

            if ( this.player.Id == playerId )
            {
                vm.IsConnectTheSamePlayer = true;
            }
            else
            {
                vm.Player = PlayersRepository.Instance.GetPlayer( playerId );
                vm.OtherPlayerName = this.player.Name;
                vm.OtherPlayerId = this.player.Id;
                vm.ShowPlayerInfo = true;

                if ( vm.Player.Connections.Contains( this.player.Id ) == true )
                    vm.IsAlreadyConnected = true;
                else
                    vm.IsConnectAnotherPlayer = true;
            }

            if ( this.regionManager.Regions[RegionNames.GamePopUpRegion].Views.Contains( this.container.Resolve<ConnectView>() ) == false )
                this.regionManager.RegisterViewWithRegion( RegionNames.GamePopUpRegion, typeof( ConnectView ) );

            this.regionManager.Regions[RegionNames.GamePopUpRegion].Activate( this.container.Resolve<ConnectView>() );

            mainView.CancelDrag();
        }

        public void PlayerConnectionRequestReceived( PlayerConnectionMessage message )
        {
            PlayerStatusManager.Instance.PauseGame();

            ConnectViewModel vm = this.container.Resolve<ConnectViewModel>();
            vm.Initialize();

            vm.Player = PlayersRepository.Instance.GetPlayer( message.SenderId );
            Debug.Assert( vm.Player != null, "There is no player with Id = " + message.SenderId + " in the local repository!" );

            Player player = PlayersRepository.Instance.GetPlayer( message.TargetId );
            Debug.Assert( vm.Player != null, "There is no player with Id = " + message.TargetId + " in the local repository!" );

            vm.OtherPlayerId = player.Id;
            vm.OtherPlayerName = player.Name;

            vm.MessageComment = message.Comments;
            vm.ShowPlayerInfo = true;
            vm.IsConnectPlayerRequest = true;

            if ( this.regionManager.Regions[RegionNames.GamePopUpRegion].Views.Contains( this.container.Resolve<ConnectView>() ) == false )
                this.regionManager.RegisterViewWithRegion( RegionNames.GamePopUpRegion, typeof( ConnectView ) );

            this.regionManager.Regions[RegionNames.GamePopUpRegion].Activate( this.container.Resolve<ConnectView>() );
        }

        public void PlayerConnectionResponseReceived( PlayerConnectionMessage message )
        {
            PlayerStatusManager.Instance.PauseGame();

            ConnectViewModel vm = this.container.Resolve<ConnectViewModel>();
            vm.Initialize();

            vm.MessageComment = message.Comments;
            vm.Player = PlayersRepository.Instance.GetPlayer( message.TargetId );

            Player player = PlayersRepository.Instance.GetPlayer( message.SenderId );
            player.Connections.Add( vm.Player.Id );
            player.PlayerConnections.Add( vm.Player );

            vm.Player.Connections.Add( player.Id );
            vm.Player.PlayerConnections.Add( player );

            vm.OtherPlayerId = this.player.Id;
            vm.OtherPlayerName = this.player.Name;

            vm.ShowPlayerInfo = true;
            vm.IsConnectPlayerResponseReceived = true;

            if ( this.regionManager.Regions[RegionNames.GamePopUpRegion].Views.Contains( this.container.Resolve<ConnectView>() ) == false )
                this.regionManager.RegisterViewWithRegion( RegionNames.GamePopUpRegion, typeof( ConnectView ) );

            this.regionManager.Regions[RegionNames.GamePopUpRegion].Activate( this.container.Resolve<ConnectView>() );

            PlayersRepository.Instance.SavePlayerConnectionId( message.SenderId, message.TargetId, null );

            // Can I Win a Connection Badge :)
            foreach ( Badge badge in BadgesRepository.Instance.Badges )
            {
                BadgeType type = BadgesRepository.Instance.BadgeTypes.Where( x => x.ID == badge.Type ).FirstOrDefault();
                Debug.Assert( type != null );

                if ( type.Key != null )
                    continue;

                if ( this.player.Connections.Count == badge.MinimumItems )
                {
                    this.player.BadgesCollection.Add( BadgesRepository.Instance.GetBadge( badge.ID ) );
                    EventService.Aggregator.GetEvent<ShowBadgesEvent>().Publish( badge.ID );
                    break;
                }
            }
        }

        public void JoinQuestReceived( Event quest )
        {
            PlayerStatusManager.Instance.PauseGame();

            JoinSupportQuestViewModel vm = this.container.Resolve<JoinSupportQuestViewModel>();
            vm.Initialize();

            vm.Quest = quest;
            vm.Sender = PlayersRepository.Instance.GetPlayer( quest.PlayerId );
            vm.HeaderText = "JOIN";
            vm.OtherPlayerId = this.player.Id;
            vm.OtherPlayerName = this.player.Name;
            vm.IsJoinQuestRequest = true;
            vm.IsCommentsVisible = true;

            if ( this.regionManager.Regions[RegionNames.GamePopUpRegion].Views.Contains( this.container.Resolve<EventsJoinOrSupportView>() ) == false )
                this.regionManager.RegisterViewWithRegion( RegionNames.GamePopUpRegion, typeof( EventsJoinOrSupportView ) );

            this.regionManager.Regions[RegionNames.GamePopUpRegion].Activate( this.container.Resolve<EventsJoinOrSupportView>() );
        }

        public void SupportQuestReceived( Event quest )
        {
            PlayerStatusManager.Instance.PauseGame();

            JoinSupportQuestViewModel vm = this.container.Resolve<JoinSupportQuestViewModel>();
            vm.Initialize();

            vm.Quest = quest;
            vm.Sender = PlayersRepository.Instance.GetPlayer( quest.PlayerId );
            vm.HeaderText = "QUESTS";
            vm.OtherPlayerId = this.player.Id;
            vm.OtherPlayerName = this.player.Name;
            vm.IsSupportQuestRequest = true;
            vm.IsCommentsVisible = true;

            if ( this.regionManager.Regions[RegionNames.GamePopUpRegion].Views.Contains( this.container.Resolve<EventsJoinOrSupportView>() ) == false )
                this.regionManager.RegisterViewWithRegion( RegionNames.GamePopUpRegion, typeof( EventsJoinOrSupportView ) );

            this.regionManager.Regions[RegionNames.GamePopUpRegion].Activate( this.container.Resolve<EventsJoinOrSupportView>() );
        }

        public void ShowBadgesPopUp( int id )
        {
            PlayerStatusManager.Instance.PauseGame();

            if ( this.regionManager.Regions[RegionNames.GamePopUpRegion].Views.Contains( this.container.Resolve<BadgeCongratsView>() ) == false )
                this.regionManager.RegisterViewWithRegion( RegionNames.GamePopUpRegion, typeof( BadgeCongratsView ) );

            this.regionManager.Regions[RegionNames.GamePopUpRegion].Activate( this.container.Resolve<BadgeCongratsView>() );

            BadgesViewModel bvm = this.container.Resolve<BadgesViewModel>();

            UserBadge badge = bvm.UserBadges.Where( x => x.Id == id ).FirstOrDefault();
            badge.IsLocked = false;

            bvm.WonBadge = badge;
            bvm.Badges.Clear();

            foreach ( UserBadge ub in bvm.UserBadges.Where( x => x.TypeId == badge.TypeId ) )
                bvm.Badges.Add( ub );

            PlayersRepository.Instance.SavePlayerBadgeId( this.player.Id, id, null );

            BadgeUpdateHolder buh = new BadgeUpdateHolder()
            {
                BadgeId = id,
                PlayerId = this.player.Id,
                MapId = this.player.MapId
            };

            this.gameModel.PublishPlayerBadgeWon( buh );
        }

        private void SubscribeForEvents()
        {
            EventService.Aggregator.GetEvent<StartGameSinglePlayerEvent>().Subscribe( this.StartSinglePlayerGame, true );
            EventService.Aggregator.GetEvent<StartGameMultiPlayerEvent>().Subscribe( this.StartMultiplayerGame, true );

            EventService.Aggregator.GetEvent<MovePlayerEvent>().Subscribe( this.MovePlayer );
            EventService.Aggregator.GetEvent<MoveEvent>().Subscribe( this.MovePlayer );


            EventService.Aggregator.GetEvent<ShowBadgesEvent>().Subscribe( this.ShowBadgesPopUp );

            EventService.Aggregator.GetEvent<ThrowRubbishEvent>().Subscribe( this.NewRubbish );

            EventService.Aggregator.GetEvent<ConnectToPlayerEvent>().Subscribe( this.ConnectWithPlayer );
        }

        private void RegisterTypes()
        {
            this.container.RegisterType<GameModuleView>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<GameMainViewModel>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<GameMainView>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<GameControlsViewModel>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<GameControlsView>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<ImageObjectView>();
            this.container.RegisterType<PopUpTrashViewModel>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<PopUpTrashView>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<GameEventsView>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<BadgesView>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<BadgesViewModel>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<ChatView>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<ChatViewModel>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<QuizView>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<TutorialView>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<TutorialViewModel>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<WallOfFameView>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<WallOfFameViewModel>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<ConnectView>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<ConnectViewModel>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<BadgeCongratsView>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<Partners>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<PartnersViewModel>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<JoinSupportQuestViewModel>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<EventsJoinOrSupportView>( new ContainerControlledLifetimeManager() );

            this.container.RegisterType<QuestsPreview>( new ContainerControlledLifetimeManager() );
            this.container.RegisterType<QuestsPreviewViewModel>( new ContainerControlledLifetimeManager() );

            //this.container.RegisterType<QuizViewModel>();

            //this.container.RegisterType<BadgesView>();
            //this.container.RegisterType<BadgesViewModel>();

            this.container.RegisterType<GameControlsExperinceView>();

            this.container.RegisterType( typeof( IDataModel ), typeof( DataModel ), new ContainerControlledLifetimeManager() );
            this.container.RegisterType( typeof( IGameModel ), typeof( GameModel ), new ContainerControlledLifetimeManager() );

            this.regionManager.RegisterViewWithRegion( RegionNames.HostRegion, typeof( GameModuleView ) );
            this.regionManager.RegisterViewWithRegion( RegionNames.GameControlsRegion, typeof( GameControlsView ) );
            this.regionManager.RegisterViewWithRegion( RegionNames.GameContentRegion, typeof( GameMainView ) );
            //this.regionManager.RegisterViewWithRegion( RegionNames.GameControlsRightRegion, typeof( GameControlsExperinceView ) );
            this.regionManager.RegisterViewWithRegion( RegionNames.ChatRegion, typeof( ChatView ) );
            this.regionManager.RegisterViewWithRegion( RegionNames.QuestsPreviewRegion, typeof( QuestsPreview ) );
        }

        private void RegisterServices()
        {

        }

        private void GameModel_NotifyPlayersReceived( ObservableCollection<Player> players )
        {
            //    if ( this.gameMode == GameMode.MultiPlayer && players.Count == 1 )
            //    {
            //        GameMainViewModel viewModel = this.container.Resolve<GameMainViewModel>();

            //        this.player.IsMainPlayer = true;
            //        viewModel.Player = this.player;

            //        viewModel.GameObjectsCollection.Add( viewModel.Player );

            //        this.StartGame();
            //    }
            //    else if ( this.gameMode == GameMode.MultiPlayer && players.Count == 2 )
            //    {
            //        if ( this.player.Name == players[0].Name )
            //            this.otherPlayer = players[1];
            //        else
            //            this.otherPlayer = players[0];

            //        //other player just joined
            //        GameMainViewModel viewModel = this.container.Resolve<GameMainViewModel>();

            //        if ( viewModel.Player == null )
            //        {
            //            this.player.IsMainPlayer = true;
            //            viewModel.Player = this.player;
            //            viewModel.GameObjectsCollection.Add( viewModel.Player );
            //        }

            //        viewModel.OtherPlayer = this.otherPlayer;
            //        viewModel.GameObjectsCollection.Add( viewModel.OtherPlayer );

            //        this.StartGame();
            //    }
            //    else if ( this.gameMode == GameMode.SinglePlayer )
            //    {
            //        this.StartGame();
            //    }
        }

        private void StartGame()
        {
            GameMainViewModel gameMain = this.container.Resolve<GameMainViewModel>();
            gameMain.IsBusy = true;

            if ( this.regionManager.Regions[RegionNames.HostRegion].Views.Contains( this.container.Resolve<GameModuleView>() ) )
                this.regionManager.Regions[RegionNames.HostRegion].Activate( this.container.Resolve<GameModuleView>() );


            this.gameModel.PlayerConnectionRequestReceived += this.PlayerConnectionRequestReceived;
            this.gameModel.PlayerConnectionResponseReceived += this.PlayerConnectionResponseReceived;

            this.gameModel.JoinQuestReceived += this.JoinQuestReceived;
            this.gameModel.SupportQuestReceived += this.SupportQuestReceived;

            QuestsManager.Instance.Initialize( this.gameModel );
        }
    }
}
